<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      //数据源

      let tempObj = {
        name: "老李",
        age: 21,
        gf: {
          gf1: {
            name: "小红",
            age: 20,
          },
          gf2: {
            name: "小芳",
            age: 20,
          },
        },
        s: Symbol("s"),
        say: function (name) {
          console.log(name);
        },
        reg: /asdasd/g,
      };
      tempObj.tempObj = tempObj; //3方法开始 极端情况 循环引用的话
      //1.JSON
      JSON.parse(JSON.stringify({}));

      //2.基础版本 及格分
      function deepClone(target) {
        if (typeof target !== "object") {
          return target;
        }
        let lastData = Array.isArray(target) ? [] : {};
        for (const key in target) {
          lastData[key] = deepClone(target[key]);
        }
        return lastData;
      }
      //3.处理循环引用 指key值能以引用地址作为key 但是会造成内存泄漏 推荐使用 WeakMap
      function deepClone2(target, map = new Map()) {
        if (typeof target === "object") {
          let cloneTarget = Array.isArray(target) ? [] : {};
          if (map.get(target)) {
            return map.get(target);
          }
          map.set(target, cloneTarget);
          for (const key in target) {
            cloneTarget[key] = deepClone2(target[key], map);
          }
          return cloneTarget;
        } else {
          return target;
        }
      }
      //4. WeakMap
      function deepClone3(target, map = new WeakMap()) {
        if (typeof target === "object") {
          let cloneTarget = Array.isArray(target) ? [] : {};
          if (map.get(target)) {
            return map.get(target);
          }
          map.set(target, cloneTarget);
          for (const key in target) {
            cloneTarget[key] = deepClone3(target[key], map);
          }
          return cloneTarget;
        } else {
          return target;
        }
      }
      console.log(deepClone3(tempObj));

      function perfectDeepClone(targetData) {
        //↓↓↓↓↓↓↓↓↓可以递归遍历的数据类型
        const mapTag = "[object Map]";
        const setTag = "[object Set]";
        const arrayTag = "[object Array]";
        const objectTag = "[object Object]";
        const argsTag = "[object Arguments]";

        //↓↓↓↓↓↓↓↓↓不可以递归遍历的数据类型
        const boolTag = "[object Boolean]";
        const dateTag = "[object Date]";
        const errorTag = "[object Error]";
        const numberTag = "[object Number]";
        const regexpTag = "[object RegExp]";
        const stringTag = "[object String]";
        const symbolTag = "[object Symbol]";
        const fucTag = "[object Function]";

        //↓↓↓↓↓↓↓↓↓可以递归遍历的数据类型的集合
        const deepTags = [mapTag, setTag, arrayTag, objectTag, argsTag];

        function isObject(target) {
          const type = typeof target;
          return target !== null && (type === "object" || type === "function");
        }
        function getType(target) {
          return Object.prototype.toString.call(target);
        }
        function getInit(target) {
          const Ctor = target.constructor;
          return new Ctor();
        }
        function cloneSymbol(targe) {
          return Object(Symbol.prototype.valueOf.call(targe));
        }
        function cloneReg(targe) {
          const reFlags = /\w*$/;
          const result = new targe.constructor(
            targe.source,
            reFlags.exec(targe)
          );
          result.lastIndex = targe.lastIndex;
          return result;
        }
        function cloneFunction(func) {
          const bodyReg = /(?<={)(.|\n)+(?=})/m;
          const paramReg = /(?<=\().+(?=\)\s+{)/;
          const funcString = func.toString();
          if (func.prototype) {
            const param = paramReg.exec(funcString);
            const body = bodyReg.exec(funcString);
            if (body) {
              if (param) {
                const paramArr = param[0].split(",");
                return new Function(...paramArr, body[0]);
              } else {
                return new Function(body[0]);
              }
            } else {
              return null;
            }
          } else {
            return eval(funcString);
          }
        }
        function cloneOtherType(targe, type) {
          const Ctor = targe.constructor;
          switch (type) {
            case boolTag:
            case numberTag:
            case stringTag:
            case errorTag:
            case fucTag:
              return cloneFunction(targe);
            case dateTag:
              return new Ctor(targe);
            case regexpTag:
              return cloneReg(targe);
            case symbolTag:
              return cloneSymbol(targe);
            default:
              return null;
          }
        }
        function deepClone(target, map = new WeakMap()) {
          if (!isObject(target)) {
            return target;
          }
          const type = getType(target);
          let cloneTarget;
          if (deepTags.includes(type)) {
            cloneTarget = getInit(target, type);
          } else {
            return cloneOtherType(target, type);
          }
          if (map.get(target)) {
            return target;
          }
          map.set(target, cloneTarget);
          if (type === setTag) {
            target.forEach((value) => {
              cloneTarget.add(deepClone(value));
            });
            return cloneTarget;
          }
          if (type === mapTag) {
            target.forEach((value, key) => {
              cloneTarget.set(key, deepClone(value));
            });
            return cloneTarget;
          }
          if (type === arrayTag || type === objectTag) {
            for (const key in target) {
              cloneTarget[key] = deepClone(target[key], map);
            }
            return cloneTarget;
          }
        }
        return deepClone(targetData);
      }
      console.log(perfectDeepClone(tempObj));
    </script>
  </body>
</html>
